using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using AloneLibrary.GameObjects;
using AloneLibrary.Services.Camera;
using AloneLibrary.Services.Input;

namespace AloneLibrary.Services.Camera
{
	/// <summary>
	/// This is a game component that implements IUpdateable.
	/// </summary>
	public class ThirdPersonCamera : Microsoft.Xna.Framework.GameComponent, ICameraHandler
	{
		private static Vector3[] cameraOffsets = new Vector3[2] {
			new Vector3(0f, 100f, 200f),
			new Vector3(20f, 70f, 70f)
		};
	
		private Matrix projectionMatrix;
		private Matrix viewMatrix;
		private Matrix rotationMatrix;
		private Vector3 cameraPosition = Vector3.Zero;
		private Vector2 cameraRotation = Vector2.Zero;
		private Vector3 cameraOffset = cameraOffsets[0];
		private bool zoomed = false;
		private GameObject focusedObject = null;


		/// <summary>
		/// Initializes a new instance of the <see cref="ThirdPersonCamera"/> class.
		/// </summary>
		/// <param name="game">Game that the game component should be attached to.</param>
		public ThirdPersonCamera(Game game)
			: base(game)
		{
			game.Services.AddService(typeof(ICameraHandler), this);
		}

		/// <summary>
		/// Gets the projection.
		/// </summary>
		/// <value>The projection.</value>
		public Matrix Projection
		{
			get { return projectionMatrix; }
		}

		/// <summary>
		/// Gets the view.
		/// </summary>
		/// <value>The view.</value>
		public Matrix View
		{
			get { return viewMatrix; }
			set { this.viewMatrix = value; }
		}

		/// <summary>
		/// Gets or sets the position.
		/// </summary>
		/// <value>The position.</value>
		public Vector3 Position
		{
			get { return cameraPosition; }
			set { cameraPosition = value; }
		}

		/// <summary>
		/// Gets or sets the rotation.
		/// </summary>
		/// <value>The rotation.</value>
		public Vector2 Rotation
		{
			get { return cameraRotation; }
			set { cameraRotation = value; }
		}

		/// <summary>
		/// Gets or sets the focused object.
		/// </summary>
		/// <value>The focused object.</value>
		public GameObject FocusedObject
		{
			get { return focusedObject; }
			set { focusedObject = value; }
		}

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="ICameraHandler"/> is zoomed.
        /// </summary>
        /// <value><c>true</c> if zoomed; otherwise, <c>false</c>.</value>
		public bool Zoomed
		{
			get { return zoomed; }
			set
			{
				zoomed = value;
				cameraOffset = cameraOffsets[Convert.ToByte(zoomed)];
			}
		}

		/// <summary>
		/// Allows the game component to perform any initialization it needs to before starting
		/// to run.  This is where it can query for any required services and load content.
		/// </summary>
		public override void Initialize()
		{
			projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Game.GraphicsDevice.Viewport.AspectRatio, 0.5f, 3000f);
			viewMatrix = Matrix.CreateLookAt(Vector3.Zero, Vector3.Forward, Vector3.Up);

			base.Initialize();
		}

        /// <summary>
        /// Called when the GameComponent needs to be updated. Override this method with component-specific update code.
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to Update</param>
		public override void Update(GameTime gameTime)
		{
			Vector3 position;
			
			if (focusedObject != null)
			{
				position = focusedObject.Position;
				Matrix.CreateFromYawPitchRoll(focusedObject.Rotation.Y, focusedObject.Rotation.X, 0f, out rotationMatrix);
			}
			else
			{
				position = Vector3.Zero;
				rotationMatrix = Matrix.Identity;
				cameraRotation.X = cameraRotation.Y = 0f;
			}

			Vector3 transformedOffset = Vector3.Transform(cameraOffset, rotationMatrix);
			Vector3 cameraPosition = transformedOffset + position;

			Vector3 transformedTarget = Vector3.Transform(Vector3.Forward, rotationMatrix);
			Vector3 finalTarget = cameraPosition + transformedTarget;

			this.viewMatrix = Matrix.CreateLookAt(cameraPosition, finalTarget, Vector3.Up);

			base.Update(gameTime);
		}
	}
}
